#include "wiiuse.h"
#include "actionset.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include <X11/Xlib.h>
#include <sys/time.h>
#include <cmath>

using namespace std;

#define STICK_MAGNITUDE_THRESHOLD 0.3 /* stick magnitude necessary to trigger event */
#define SHOULDER_MAGNITUDE_THRESHOLD 0.3 /* shoulder button pressure necessary to trigger event */
#define STICK_ANGLE_THRESHOLD 65 /* degrees away from actuall 4 point compass direction the stick 
                                    can be and still activate the corresponding event */
#define ROLL_THRESHOLD 15 /* in degrees, same for PITCH_THRESHOLD */
#define PITCH_THRESHOLD 10
#define FLICK_THRESHOLD 4 /* amount of gforce necessary to trigger the flick event */
#define NC_FLICK_THRESHOLD 0.6 /* amount of gforce necessary to trigger the flick event on the nunchuk */
#define WHAMMY_THRESHOLD 0.5 /* amount of pressure necessary to trigger a whammy button event (0 - 1)*/
#define FRAME_DURATION 10000

#define SCAN_TIME 5 /* number of seconds each scan for a Wii Remote should last */

long int get_time();
void handle_status(struct wiimote_t* wm);
void handle_event(struct wiimote_t* wm);
void handle_disconnect(wiimote* wm);
void process_mouse_instructions();
void toggle_wiimote_motion_sensing();

Display *display;
Window winRoot;
wiimote** wiimotes;
ActionSet actions; // needs to be accessible from handle_event()
CC_ActionSet ccactions; // needs to be accessible from handle_event()
NC_ActionSet ncactions; // needs to be accessible from handle_event()
GTR_ActionSet gtractions; // needs to be accessible from handle_event()
bool show_battery = true; // needs to be accessible from handle_status()

typedef enum { NUNCHUK, CLASSIC, GUITAR, NONE } ExpansionType;
ExpansionType expansion_type = NONE;

bool KEEP_RUNNING = 1;
bool WIIMOTE_ACC_MOUSE = false;
bool WIIMOTE_IR_MOUSE = false;
/* USED FOR CRAPPY CODE
bool CC_LJS_MOUSE = false; */

int mouse_x = 0;
int mouse_y = 0;
int STICK_SENSITIVITY = 10;
int ACCELEROMETER_SENSITIVITY = 10; // Lower is more sensitive

int main(int argc, char *argv[])
{
// Check usage
if (argc != 2)
	{
	cout << "Correct usage:\n\txwii profile_filename" << endl;
	return 1;
	}

// Initialise X11 Low-level variables
display = XOpenDisplay(0);
if(display == NULL)
	return 1;
winRoot = XDefaultRootWindow(display);

wiimotes = wiiuse_init(1);
cout << "Hold 1 + 2 to put the Wii Remote in discoverable mode." << endl;
int found = wiiuse_find(wiimotes, 1, SCAN_TIME);

int connected = wiiuse_connect(wiimotes, 1);
if (connected)
    {
    cout << "Connected to 1 Wiimote." << endl;
    show_battery = true; // so that initial status message will display battery level on stdout
    }
else
    { 
    cerr << "Failed to connect to any wiimote." << endl;
    return 1;
    }

// Initialize ActionSet
ifstream profile;
profile.open(argv[1], ios::in);
if (!profile.is_open())
    {
    cout << "Could not open specified config file for reading.  Exiting..." << endl;
    exit(1);
    }
string profile_data = "";
string line;
while (!profile.eof())
    {
    getline(profile, line);
    profile_data += line + "\n";
    }

profile.close();
actions = ActionSet(profile_data);
ccactions = CC_ActionSet(profile_data);
ncactions = NC_ActionSet(profile_data);
gtractions = GTR_ActionSet(profile_data);

wiiuse_motion_sensing(wiimotes[0], 1);

// Begin Main Input Loop
long int new_time, old_time = get_time();
CC_InputState ccistate;
NC_InputState ncistate;
GTR_InputState gtristate;
while (KEEP_RUNNING)
    {
    if (wiiuse_poll(wiimotes, 1))
        {
        // polling the wiimote reported an event
        switch (wiimotes[0]->event)
            {
            case WIIUSE_NUNCHUK_INSERTED:
                expansion_type = NUNCHUK;
                cout << "Nunchuk Inserted" << endl;
                break;
            case WIIUSE_CLASSIC_CTRL_INSERTED:
                expansion_type = CLASSIC;
                cout << "Classic Inserted" << endl;
                break;
            case WIIUSE_GUITAR_HERO_3_CTRL_INSERTED:
                expansion_type = GUITAR;
                cout << "Guitar Inserted" << endl;
                break;
            case WIIUSE_GUITAR_HERO_3_CTRL_REMOVED:
                gtristate = GTR_InputState(true);
                xwii_process_GTR_input(gtristate, gtractions);
                expansion_type = NONE;
                cout << "Guitar Removed" << endl;
                break;
            case WIIUSE_NUNCHUK_REMOVED:
                ncistate = NC_InputState(true);
                xwii_process_NC_input(ncistate, ncactions);
                expansion_type = NONE;
                cout << "Nunchuk Removed" << endl;
                break;
            case WIIUSE_CLASSIC_CTRL_REMOVED:
                ccistate = CC_InputState(true);
                xwii_process_CC_input(ccistate, ccactions);
                expansion_type = NONE;
                cout << "Classic Controller Removed" << endl;
                break;
            case WIIUSE_STATUS:
                handle_status(wiimotes[0]);
                break;
            case WIIUSE_EVENT:
                handle_event(wiimotes[0]);
                break;
            case WIIUSE_DISCONNECT:
                handle_disconnect(wiimotes[0]);
                break;
            default:
                break;
            }
        }
    new_time = get_time();
    if ((new_time - old_time) > FRAME_DURATION)
        {
        process_mouse_instructions();
        old_time = new_time;
        }
    }

// End program
wiiuse_cleanup(wiimotes, 1);
XCloseDisplay(display);
return 0;
}

long int get_time()
{
static struct timeval mytime;
gettimeofday(&mytime, NULL);
long int returnable = (mytime.tv_sec * 1000000) + mytime.tv_usec;
return returnable;
}

void handle_event(struct wiimote_t* wm)
{
if (WIIMOTE_ACC_MOUSE)
    {
    if (wm->orient.roll > ROLL_THRESHOLD || wm->orient.roll < (-1 * ROLL_THRESHOLD))
        mouse_x = int(wm->orient.roll) / ACCELEROMETER_SENSITIVITY;
    else
        mouse_x = 0;
    if (wm->orient.pitch > PITCH_THRESHOLD || wm->orient.pitch < (-1 * PITCH_THRESHOLD))
        mouse_y = int(wm->orient.pitch) / ACCELEROMETER_SENSITIVITY;
    else
        mouse_y = 0;
    }

if (WIIMOTE_IR_MOUSE)
    {
    mouse_x = wm->ir.x;
    mouse_y = wm->ir.y;
    }

/* Variables used to track state of non-button inputs using button-like interface (eg. shoulder buttons) */
static bool L_PRESSED = false;
static bool R_PRESSED = false;
static bool LS_UP_PRESSED = false;
static bool LS_DOWN_PRESSED = false;
static bool LS_LEFT_PRESSED = false;
static bool LS_RIGHT_PRESSED = false;
static bool RS_UP_PRESSED = false;
static bool RS_DOWN_PRESSED = false;
static bool RS_LEFT_PRESSED = false;
static bool RS_RIGHT_PRESSED = false;
static bool MIDFLICK = false;
static bool NC_UP_PRESSED = false;
static bool NC_DOWN_PRESSED = false;
static bool NC_LEFT_PRESSED = false;
static bool NC_RIGHT_PRESSED = false;
static bool NC_MIDFLICK = false;
static bool GTR_UP_PRESSED = false;
static bool GTR_DOWN_PRESSED = false;
static bool GTR_LEFT_PRESSED = false;
static bool GTR_RIGHT_PRESSED = false;
static bool WHAMMY_PRESSED = false;

float floor = 0.0, ceiling = 0.0;
int theta, quadrant;

InputState istate = InputState();

if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_A))		{ istate.a = true; istate.a_type = PRESSED;}
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_B))		{ istate.b = true; istate.b_type = PRESSED; }
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_UP))		{ istate.up = true; istate.up_type = PRESSED; }
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_DOWN))	{ istate.down = true; istate.down_type = PRESSED; }
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_LEFT))	{ istate.left = true; istate.left_type = PRESSED; }
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_RIGHT))	{ istate.right = true; istate.right_type = PRESSED; }
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_MINUS))	{ istate.minus = true; istate.minus_type = PRESSED; }
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_PLUS))	{ istate.plus = true; istate.plus_type = PRESSED; }
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_ONE))	{ istate.one = true; istate.one_type = PRESSED; }
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_TWO))	{ istate.two = true; istate.two_type = PRESSED; }
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_HOME))	{ istate.home = true; istate.home_type = PRESSED; }

if (IS_RELEASED(wm, WIIMOTE_BUTTON_A))		{ istate.a = true; istate.a_type = RELEASED;}
if (IS_RELEASED(wm, WIIMOTE_BUTTON_B))		{ istate.b = true; istate.b_type = RELEASED; };
if (IS_RELEASED(wm, WIIMOTE_BUTTON_UP))		{ istate.up = true; istate.up_type = RELEASED; }
if (IS_RELEASED(wm, WIIMOTE_BUTTON_DOWN))	{ istate.down = true; istate.down_type = RELEASED; }
if (IS_RELEASED(wm, WIIMOTE_BUTTON_LEFT))	{ istate.left = true; istate.left_type = RELEASED; }
if (IS_RELEASED(wm, WIIMOTE_BUTTON_RIGHT))	{ istate.right = true; istate.right_type = RELEASED; }
if (IS_RELEASED(wm, WIIMOTE_BUTTON_MINUS))	{ istate.minus = true; istate.minus_type = RELEASED; }
if (IS_RELEASED(wm, WIIMOTE_BUTTON_PLUS))	{ istate.plus = true; istate.plus_type = RELEASED; }
if (IS_RELEASED(wm, WIIMOTE_BUTTON_ONE))	{ istate.one = true; istate.one_type = RELEASED; }
if (IS_RELEASED(wm, WIIMOTE_BUTTON_TWO))	{ istate.two = true; istate.two_type = RELEASED; }
if (IS_RELEASED(wm, WIIMOTE_BUTTON_HOME))	{ istate.home = true; istate.home_type = RELEASED; }

if (
    (wm->gforce.x > FLICK_THRESHOLD ||
    wm->gforce.y > FLICK_THRESHOLD ||   // if a flick has occurred
    wm->gforce.z > FLICK_THRESHOLD ||
    (-1 * wm->gforce.x) < (-1 * FLICK_THRESHOLD) ||
    (-1 * wm->gforce.y) < (-1 * FLICK_THRESHOLD) ||
    (-1 * wm->gforce.z) < (-1 * FLICK_THRESHOLD))
    &&
    !MIDFLICK)
    {
    istate.flick = true; // so that flick event is acted upon
    istate.flick_type = PRESSED;
    MIDFLICK = true; // so that another flick event isn't acted upon until another flick happens
    }
else if (
    (wm->gforce.x <= FLICK_THRESHOLD &&
    wm->gforce.y <= FLICK_THRESHOLD &&   // if all gforces are below threshold
    wm->gforce.z <= FLICK_THRESHOLD &&
    (-1 * wm->gforce.x) >= (-1 * FLICK_THRESHOLD) &&
    (-1 * wm->gforce.y) >= (-1 * FLICK_THRESHOLD) &&
    (-1 * wm->gforce.z) >= (-1 * FLICK_THRESHOLD))
    &&
    MIDFLICK)
    {
    istate.flick = true; // so that flick event is acted upon
    istate.flick_type = RELEASED;
    MIDFLICK = false;
    }

xwii_process_input(istate, actions);

// Events related to controller expansions (Nunchuk, Classic Controller and Guitar)
    if (expansion_type == CLASSIC)
    {
    /* Classic Controller or Wii Guitar */
    CC_InputState ccistate = CC_InputState(); 

    struct classic_ctrl_t* cc = (classic_ctrl_t*)&wm->exp.classic;
    if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_A))		{ ccistate.a = true; ccistate.a_type = PRESSED; }
    if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_B))		{ ccistate.b = true; ccistate.b_type = PRESSED; }
    if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_X))		{ ccistate.x = true; ccistate.x_type = PRESSED; }
    if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_Y))		{ ccistate.y = true; ccistate.y_type = PRESSED; }
    if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_HOME))	{ ccistate.home = true; ccistate.home_type = PRESSED; }
	if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_ZL))	{ ccistate.zl = true; ccistate.zl_type = PRESSED; }
	if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_ZR))	{ ccistate.zr = true; ccistate.zr_type = PRESSED; }
	if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_LEFT))	{ ccistate.d_left = true; ccistate.d_left_type = PRESSED; }
	if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_UP))	{ ccistate.d_up = true; ccistate.d_up_type = PRESSED; }
	if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_RIGHT))	{ ccistate.d_right = true; ccistate.d_right_type = PRESSED; }
	if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_DOWN))	{ ccistate.d_down = true; ccistate.d_down_type = PRESSED; }
	if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_MINUS))	{ ccistate.minus = true; ccistate.minus_type = PRESSED; }
	if (IS_JUST_PRESSED(cc, CLASSIC_CTRL_BUTTON_PLUS))	{ ccistate.plus = true; ccistate.plus_type = PRESSED; }

    if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_A))		    { ccistate.a = true; ccistate.a_type = RELEASED; }
    if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_B))	    	{ ccistate.b = true; ccistate.b_type = RELEASED; }
    if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_X))		    { ccistate.x = true; ccistate.x_type = RELEASED; }
    if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_Y))		    { ccistate.y = true; ccistate.y_type = RELEASED; }
    if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_HOME))	    { ccistate.home = true; ccistate.home_type = RELEASED; }
	if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_ZL))	    { ccistate.zl = true; ccistate.zl_type = RELEASED; }
	if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_ZR))	    { ccistate.zr = true; ccistate.zr_type = RELEASED; }
	if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_LEFT))	    { ccistate.d_left = true; ccistate.d_left_type = RELEASED; }
	if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_UP))	    { ccistate.d_up = true; ccistate.d_up_type = RELEASED; }
	if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_RIGHT))	    { ccistate.d_right = true; ccistate.d_right_type = RELEASED; }
	if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_DOWN))	    { ccistate.d_down = true; ccistate.d_down_type = RELEASED; }
	if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_MINUS))	    { ccistate.minus = true; ccistate.minus_type = RELEASED; }
	if (IS_RELEASED(cc, CLASSIC_CTRL_BUTTON_PLUS))	    { ccistate.plus = true; ccistate.plus_type = RELEASED; }
	
	/* Classic Controller Sticks and Shoulder Buttons */
	if (cc->l_shoulder >= SHOULDER_MAGNITUDE_THRESHOLD && L_PRESSED == false) 
	    { 
	    ccistate.l = true;
	    ccistate.l_type = PRESSED;
	    L_PRESSED = true;
	    }
	else if (cc->l_shoulder < SHOULDER_MAGNITUDE_THRESHOLD && L_PRESSED == true)
	    {
	    ccistate.l = true;
	    ccistate.l_type = RELEASED;
	    L_PRESSED = false;
	    }
	if (cc->r_shoulder >= SHOULDER_MAGNITUDE_THRESHOLD && R_PRESSED == false) 
	    { 
	    ccistate.r = true;
	    ccistate.r_type = PRESSED;
	    R_PRESSED = true;
	    }
	else if (cc->r_shoulder < SHOULDER_MAGNITUDE_THRESHOLD && R_PRESSED == true)
	    {
	    ccistate.r = true;
	    ccistate.r_type = RELEASED;
	    R_PRESSED = false;
	    }

    
    if (cc->ljs.mag >= STICK_MAGNITUDE_THRESHOLD)
        {
        floor = cc->ljs.ang - STICK_ANGLE_THRESHOLD;
        ceiling = cc->ljs.ang + STICK_ANGLE_THRESHOLD;
        if (floor <= 90 && 90 <= ceiling && LS_RIGHT_PRESSED == false)
            {
            ccistate.ls_right = true;
            ccistate.ls_right_type = PRESSED;
            LS_RIGHT_PRESSED = true;
            }
        else if (!(floor <= 90 && 90 <= ceiling) && LS_RIGHT_PRESSED == true)
            {
            ccistate.ls_right = true;
            ccistate.ls_right_type = RELEASED;
            LS_RIGHT_PRESSED = false;
            }
        if (floor <= 180 && 180 <= ceiling && LS_DOWN_PRESSED == false)
            {
            ccistate.ls_down = true;
            ccistate.ls_down_type = PRESSED;
            LS_DOWN_PRESSED = true;
            }
        else if (!(floor <= 180 && 180 <= ceiling) && LS_DOWN_PRESSED == true)
            {
            ccistate.ls_down = true;
            ccistate.ls_down_type = RELEASED;
            LS_DOWN_PRESSED = false;
            }
        if (floor <= 270 && 270 <= ceiling && LS_LEFT_PRESSED == false)
            {
            ccistate.ls_left = true;
            ccistate.ls_left_type = PRESSED;
            LS_LEFT_PRESSED = true;
            }
        else if (!(floor <= 270 && 270 <= ceiling) && LS_LEFT_PRESSED == true)
            {
            ccistate.ls_left = true;
            ccistate.ls_left_type = RELEASED;
            LS_LEFT_PRESSED = false;
            }
        if ((floor <= 0 || ceiling >= 360) && LS_UP_PRESSED == false)
            {
            ccistate.ls_up = true;
            ccistate.ls_up_type = PRESSED;
            LS_UP_PRESSED = true;
            }
        else if (!(floor <= 0 || ceiling >= 360) && LS_UP_PRESSED == true)
            {
            ccistate.ls_up = true;
            ccistate.ls_up_type = RELEASED;
            LS_UP_PRESSED = false;
            }
        }
    else
        {
        if (LS_RIGHT_PRESSED)
            { 
            LS_RIGHT_PRESSED = false; 
            ccistate.ls_right = true;
            ccistate.ls_right_type = RELEASED;
            }
        if (LS_DOWN_PRESSED)
            { 
            LS_DOWN_PRESSED = false; 
            ccistate.ls_down = true;
            ccistate.ls_down_type = RELEASED;
            }
        if (LS_LEFT_PRESSED)
            { 
            LS_LEFT_PRESSED = false; 
            ccistate.ls_left = true;
            ccistate.ls_left_type = RELEASED;
            }
        if (LS_UP_PRESSED)
            { 
            LS_UP_PRESSED = false; 
            ccistate.ls_up = true;
            ccistate.ls_up_type = RELEASED;
            }
        }
    
    if (cc->rjs.mag >= STICK_MAGNITUDE_THRESHOLD)
        {
        floor = cc->rjs.ang - STICK_ANGLE_THRESHOLD;
        ceiling = cc->rjs.ang + STICK_ANGLE_THRESHOLD;
        if (floor <= 90 && 90 <= ceiling && RS_RIGHT_PRESSED == false)
            {
            ccistate.rs_right = true;
            ccistate.rs_right_type = PRESSED;
            RS_RIGHT_PRESSED = true;
            }
        else if (!(floor <= 90 && 90 <= ceiling) && RS_RIGHT_PRESSED == true)
            {
            ccistate.rs_right = true;
            ccistate.rs_right_type = RELEASED;
            RS_RIGHT_PRESSED = false;
            }
        if (floor <= 180 && 180 <= ceiling && RS_DOWN_PRESSED == false)
            {
            ccistate.rs_down = true;
            ccistate.rs_down_type = PRESSED;
            RS_DOWN_PRESSED = true;
            }
        else if (!(floor <= 180 && 180 <= ceiling) && RS_DOWN_PRESSED == true)
            {
            ccistate.rs_down = true;
            ccistate.rs_down_type = RELEASED;
            RS_DOWN_PRESSED = false;
            }
        if (floor <= 270 && 270 <= ceiling && RS_LEFT_PRESSED == false)
            {
            ccistate.rs_left = true;
            ccistate.rs_left_type = PRESSED;
            RS_LEFT_PRESSED = true;
            }
        else if (!(floor <= 270 && 270 <= ceiling) && RS_LEFT_PRESSED == true)
            {
            ccistate.rs_left = true;
            ccistate.rs_left_type = RELEASED;
            RS_LEFT_PRESSED = false;
            }
        if ((floor <= 0 || ceiling >= 360) && RS_UP_PRESSED == false)
            {
            ccistate.rs_up = true;
            ccistate.rs_up_type = PRESSED;
            RS_UP_PRESSED = true;
            }
        else if (!(floor <= 0 || ceiling >= 360) && RS_UP_PRESSED == true)
            {
            ccistate.rs_up = true;
            ccistate.rs_up_type = RELEASED;
            RS_UP_PRESSED = false;
            }
        }
    else
        {
        if (RS_RIGHT_PRESSED)
            { 
            RS_RIGHT_PRESSED = false; 
            ccistate.rs_right = true;
            ccistate.rs_right_type = RELEASED;
            }
        if (RS_DOWN_PRESSED)
            { 
            RS_DOWN_PRESSED = false; 
            ccistate.rs_down = true;
            ccistate.rs_down_type = RELEASED;
            }
        if (RS_LEFT_PRESSED)
            { 
            RS_LEFT_PRESSED = false; 
            ccistate.rs_left = true;
            ccistate.rs_left_type = RELEASED;
            }
        if (RS_UP_PRESSED)
            { 
            RS_UP_PRESSED = false; 
            ccistate.rs_up = true;
            ccistate.rs_up_type = RELEASED;
            }
        }
		
    
    xwii_process_CC_input(ccistate, ccactions);
    } // if expansion_type == CLASSIC
    
    /* Nunchuk */
if (expansion_type == NUNCHUK)
    {
    struct nunchuk_t* nc = (nunchuk_t*)&wm->exp.nunchuk;
    NC_InputState ncistate = NC_InputState();
    
    /* Fill out the NC_InputState object */
    if (IS_JUST_PRESSED(nc, NUNCHUK_BUTTON_C))		{ ncistate.c = true; ncistate.c_type = PRESSED; }
    if (IS_JUST_PRESSED(nc, NUNCHUK_BUTTON_Z))		{ ncistate.z = true; ncistate.z_type = PRESSED; }
    
    if (IS_RELEASED(nc, NUNCHUK_BUTTON_C))  		{ ncistate.c = true; ncistate.c_type = RELEASED; }
    if (IS_RELEASED(nc, NUNCHUK_BUTTON_Z))  		{ ncistate.z = true; ncistate.z_type = RELEASED; }

    if (nc->js.mag >= STICK_MAGNITUDE_THRESHOLD)
        {
        floor = nc->js.ang - STICK_ANGLE_THRESHOLD;
        ceiling = nc->js.ang + STICK_ANGLE_THRESHOLD;
        if (floor <= 90 && 90 <= ceiling && NC_RIGHT_PRESSED == false)
            {
            ncistate.stick_right = true;
            ncistate.stick_right_type = PRESSED;
            NC_RIGHT_PRESSED = true;
            }
        else if (!(floor <= 90 && 90 <= ceiling) && NC_RIGHT_PRESSED == true)
            {
            ncistate.stick_right = true;
            ncistate.stick_right_type = RELEASED;
            NC_RIGHT_PRESSED = false;
            }
        if (floor <= 180 && 180 <= ceiling && NC_DOWN_PRESSED == false)
            {
            ncistate.stick_down = true;
            ncistate.stick_down_type = PRESSED;
            NC_DOWN_PRESSED = true;
            }
        else if (!(floor <= 180 && 180 <= ceiling) && NC_DOWN_PRESSED == true)
            {
            ncistate.stick_down = true;
            ncistate.stick_down_type = RELEASED;
            NC_DOWN_PRESSED = false;
            }
        if (floor <= 270 && 270 <= ceiling && NC_LEFT_PRESSED == false)
            {
            ncistate.stick_left = true;
            ncistate.stick_left_type = PRESSED;
            NC_LEFT_PRESSED = true;
            }
        else if (!(floor <= 270 && 270 <= ceiling) && NC_LEFT_PRESSED == true)
            {
            ncistate.stick_left = true;
            ncistate.stick_left_type = RELEASED;
            NC_LEFT_PRESSED = false;
            }
        if ((floor <= 0 || ceiling >= 360) && NC_UP_PRESSED == false)
            {
            ncistate.stick_up = true;
            ncistate.stick_up_type = PRESSED;
            NC_UP_PRESSED = true;
            }
        else if (!(floor <= 0 || ceiling >= 360) && NC_UP_PRESSED == true)
            {
            ncistate.stick_up = true;
            ncistate.stick_up_type = RELEASED;
            NC_UP_PRESSED = false;
            }
        }
    else
        {
        if (NC_RIGHT_PRESSED)
            { 
            NC_RIGHT_PRESSED = false; 
            ncistate.stick_right = true;
            ncistate.stick_right_type = RELEASED;
            }
        if (NC_DOWN_PRESSED)
            { 
            NC_DOWN_PRESSED = false; 
            ncistate.stick_down = true;
            ncistate.stick_down_type = RELEASED;
            }
        if (NC_LEFT_PRESSED)
            { 
            NC_LEFT_PRESSED = false; 
            ncistate.stick_left = true;
            ncistate.stick_left_type = RELEASED;
            }
        if (NC_UP_PRESSED)
            { 
            NC_UP_PRESSED = false; 
            ncistate.stick_up = true;
            ncistate.stick_up_type = RELEASED;
            }
        }    

    if (
        (nc->gforce.x > NC_FLICK_THRESHOLD ||
        nc->gforce.y > NC_FLICK_THRESHOLD ||   // if a flick has occurred
        nc->gforce.z > NC_FLICK_THRESHOLD ||
        (-1 * nc->gforce.x) < (-1 * NC_FLICK_THRESHOLD) ||
        (-1 * nc->gforce.y) < (-1 * NC_FLICK_THRESHOLD) ||
        (-1 * nc->gforce.z) < (-1 * NC_FLICK_THRESHOLD))
        &&
        !NC_MIDFLICK)
        {
        ncistate.flick = true; // so that flick event is acted upon
        ncistate.flick_type = PRESSED;
        NC_MIDFLICK = true; // so that another flick event isn't acted upon until another flick happens
        }
    else if (
        (nc->gforce.x <= NC_FLICK_THRESHOLD &&
        nc->gforce.y <= NC_FLICK_THRESHOLD &&   // if all gforces are below threshold
        nc->gforce.z <= NC_FLICK_THRESHOLD &&
        (-1 * nc->gforce.x) >= (-1 * NC_FLICK_THRESHOLD) &&
        (-1 * nc->gforce.y) >= (-1 * NC_FLICK_THRESHOLD) &&
        (-1 * nc->gforce.z) >= (-1 * NC_FLICK_THRESHOLD))
        &&
        NC_MIDFLICK)
        {
        ncistate.flick = true; // so that flick event is acted upon
        ncistate.flick_type = RELEASED;
        NC_MIDFLICK = false;
        }
    
    //cout << "Nunchuk G Forces:" << endl 
    //     << "\tX: " << nc->gforce.x << endl
    //     << "\tY: " << nc->gforce.y << endl
    //     << "\tZ: " << nc->gforce.z << endl;

    xwii_process_NC_input(ncistate, ncactions);
    } // if expansion_type == NUNCHUK
    
if (expansion_type == GUITAR)
    {
    GTR_InputState gtristate = GTR_InputState();
    struct guitar_hero_3_t* gtr = (guitar_hero_3_t*)&wm->exp.gh3;
    
    if (IS_JUST_PRESSED(gtr, GUITAR_HERO_3_BUTTON_GREEN))   { gtristate.green = true; gtristate.green_type = PRESSED; }
    if (IS_JUST_PRESSED(gtr, GUITAR_HERO_3_BUTTON_RED))   { gtristate.red = true; gtristate.red_type = PRESSED; }
    if (IS_JUST_PRESSED(gtr, GUITAR_HERO_3_BUTTON_YELLOW))   { gtristate.yellow = true; gtristate.yellow_type = PRESSED; }
    if (IS_JUST_PRESSED(gtr, GUITAR_HERO_3_BUTTON_BLUE))   { gtristate.blue = true; gtristate.blue_type = PRESSED; }
    if (IS_JUST_PRESSED(gtr, GUITAR_HERO_3_BUTTON_ORANGE))   { gtristate.orange = true; gtristate.orange_type = PRESSED; }
    
    if (IS_JUST_PRESSED(gtr, GUITAR_HERO_3_BUTTON_STRUM_UP))   { gtristate.strum_up = true; gtristate.strum_up_type = PRESSED; }
    if (IS_JUST_PRESSED(gtr, GUITAR_HERO_3_BUTTON_STRUM_DOWN))   { gtristate.strum_down = true; gtristate.strum_down_type = PRESSED; }
    if (IS_JUST_PRESSED(gtr, GUITAR_HERO_3_BUTTON_PLUS))   { gtristate.plus = true; gtristate.plus_type = PRESSED; }
    if (IS_JUST_PRESSED(gtr, GUITAR_HERO_3_BUTTON_MINUS))   { gtristate.minus = true; gtristate.minus_type = PRESSED; }

    if (WHAMMY_PRESSED == false && gtr->whammy_bar >= WHAMMY_THRESHOLD)
        {
        gtristate.whammy = true;
        gtristate.whammy_type = PRESSED;
        WHAMMY_PRESSED = true;
        }

    if (IS_RELEASED(gtr, GUITAR_HERO_3_BUTTON_GREEN))   { gtristate.green = true; gtristate.green_type = RELEASED; }
    if (IS_RELEASED(gtr, GUITAR_HERO_3_BUTTON_RED))   { gtristate.red = true; gtristate.red_type = RELEASED; }
    if (IS_RELEASED(gtr, GUITAR_HERO_3_BUTTON_YELLOW))   { gtristate.yellow = true; gtristate.yellow_type = RELEASED; }
    if (IS_RELEASED(gtr, GUITAR_HERO_3_BUTTON_BLUE))   { gtristate.blue = true; gtristate.blue_type = RELEASED; }
    if (IS_RELEASED(gtr, GUITAR_HERO_3_BUTTON_ORANGE))   { gtristate.orange = true; gtristate.orange_type = RELEASED; }
    
    if (IS_RELEASED(gtr, GUITAR_HERO_3_BUTTON_STRUM_UP))   { gtristate.strum_up = true; gtristate.strum_up_type = RELEASED; }
    if (IS_RELEASED(gtr, GUITAR_HERO_3_BUTTON_STRUM_DOWN))   { gtristate.strum_down = true; gtristate.strum_down_type = RELEASED; }
    if (IS_RELEASED(gtr, GUITAR_HERO_3_BUTTON_PLUS))   { gtristate.plus = true; gtristate.plus_type = RELEASED; }
    if (IS_RELEASED(gtr, GUITAR_HERO_3_BUTTON_MINUS))   { gtristate.minus = true; gtristate.minus_type = RELEASED; }

    if (WHAMMY_PRESSED == true && gtr->whammy_bar < WHAMMY_THRESHOLD)
        {
        gtristate.whammy = true;
        gtristate.whammy_type = RELEASED;
        WHAMMY_PRESSED = false;
        }
    
    if (gtr->js.mag >= STICK_MAGNITUDE_THRESHOLD)
        {
        floor = gtr->js.ang - STICK_ANGLE_THRESHOLD;
        ceiling = gtr->js.ang + STICK_ANGLE_THRESHOLD;
        if (floor <= 90 && 90 <= ceiling && GTR_RIGHT_PRESSED == false)
            {
            gtristate.js_right = true;
            gtristate.js_right_type = PRESSED;
            GTR_RIGHT_PRESSED = true;
            }
        else if (!(floor <= 90 && 90 <= ceiling) && GTR_RIGHT_PRESSED == true)
            {
            gtristate.js_right = true;
            gtristate.js_right_type = RELEASED;
            GTR_RIGHT_PRESSED = false;
            }
        if (floor <= 180 && 180 <= ceiling && GTR_DOWN_PRESSED == false)
            {
            gtristate.js_down = true;
            gtristate.js_down_type = PRESSED;
            GTR_DOWN_PRESSED = true;
            }
        else if (!(floor <= 180 && 180 <= ceiling) && GTR_DOWN_PRESSED == true)
            {
            gtristate.js_down = true;
            gtristate.js_down_type = RELEASED;
            GTR_DOWN_PRESSED = false;
            }
        if (floor <= 270 && 270 <= ceiling && GTR_LEFT_PRESSED == false)
            {
            gtristate.js_left = true;
            gtristate.js_left_type = PRESSED;
            GTR_LEFT_PRESSED = true;
            }
        else if (!(floor <= 270 && 270 <= ceiling) && GTR_LEFT_PRESSED == true)
            {
            gtristate.js_left = true;
            gtristate.js_left_type = RELEASED;
            GTR_LEFT_PRESSED = false;
            }
        if ((floor <= 0 || ceiling >= 360) && GTR_UP_PRESSED == false)
            {
            gtristate.js_up = true;
            gtristate.js_up_type = PRESSED;
            GTR_UP_PRESSED = true;
            }
        else if (!(floor <= 0 || ceiling >= 360) && GTR_UP_PRESSED == true)
            {
            gtristate.js_up = true;
            gtristate.js_up_type = RELEASED;
            GTR_UP_PRESSED = false;
            }
        }
    else
        {
        if (GTR_RIGHT_PRESSED)
            { 
            GTR_RIGHT_PRESSED = false; 
            gtristate.js_right = true;
            gtristate.js_right_type = RELEASED;
            }
        if (GTR_DOWN_PRESSED)
            { 
            GTR_DOWN_PRESSED = false; 
            gtristate.js_down = true;
            gtristate.js_down_type = RELEASED;
            }
        if (GTR_LEFT_PRESSED)
            { 
            GTR_LEFT_PRESSED = false; 
            gtristate.js_left = true;
            gtristate.js_left_type = RELEASED;
            }
        if (GTR_UP_PRESSED)
            { 
            GTR_UP_PRESSED = false; 
            gtristate.js_up = true;
            gtristate.js_up_type = RELEASED;
            }
        }
        
    xwii_process_GTR_input(gtristate, gtractions);
    } // if epansion_type == GUITAR
return;
}

void handle_status(struct wiimote_t* wm)
{
if (show_battery)
    {
    cout << "Battery Level: " << (wm->battery_level*100) << "%" << endl;
    show_battery = false;
    }
cout << "Wiimote status changed." << endl;
return;
}

void handle_disconnect(wiimote* wm) {
cout << "Wii Remote was Disconnected.  Waiting for a reconnect... (Hold 1 + 2)"
     << endl << endl;
int found = 0;
while (found == 0) 
    {
    found = wiiuse_find(wiimotes, 1, SCAN_TIME);
    }
int connected = wiiuse_connect(wiimotes, 1);
if (connected)
    {
    cout << "Connected to 1 Wiimote." << endl;
    show_battery = true; // so that initial status message will display battery level on stdout
    }
}

void process_mouse_instructions()
{
if (WIIMOTE_ACC_MOUSE)
XIMovePointer(mouse_x, mouse_y, true); // relative movement
else if (WIIMOTE_IR_MOUSE)
XIMovePointer(mouse_x, mouse_y, false); // absolute movement
return;
}

